﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Serialization;
using System.Xml;

namespace WowTourGuideTranslator
{
    /// <summary>
    /// parses wowhead for a given quest
    /// </summary>
    [Serializable]
    [XmlRoot]
    public class WowheadQuestParser : WowheadBaseParser
    {
        #region declarations

        /// <summary>
        /// Helper class for quest items and kills incl. quantity (translated)
        /// </summary>
        [Serializable]
        public class Objective : WowheadBaseParser.TranslatedWowheadData
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="Objective"/> class.
            /// </summary>
            public Objective()
            {
                this.KillText = new TranslatedPair();
            }

            /// <summary>
            /// Gets or sets the count.
            /// </summary>
            /// <value>The count.</value>
            public int Count { get; set; }

            /// <summary>
            /// Gets or sets the kill text.
            /// </summary>
            /// <value>The kill text.</value>
            public TranslatedPair KillText { get; set; }
        }

        #endregion declarations

        #region fields

        #endregion fields

        #region constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="WowheadQuestParser"/> class.
        /// </summary>
        public WowheadQuestParser() 
        {

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="QuestParser"/> class.
        /// </summary>
        /// <param name="questId">The quest id.</param>
        /// <param name="lang">The language.</param>
        private WowheadQuestParser(int questId, string language)
            : base(RequestType.Quest, questId, language)
        {
        }

        #endregion constructor

        #region methods

        /// <summary>
        /// Parses wowhead for the specified quest id.
        /// </summary>
        /// <param name="questId">The quest id.</param>
        /// <param name="language">The language.</param>
        /// <returns></returns>
        static internal WowheadQuestParser Parse(int questId, string language)
        {
            WowheadQuestParser questParser = new WowheadQuestParser(questId, language);

            if (!DataProvider.QuestDict.ContainsKey(questId))
            {
                DataProvider.QuestDict.Add(questId, questParser);
            }

            return questParser;
        }

        /// <summary>
        /// Parses the pages.
        /// </summary>
        protected override void ParsePages()
        {
            this.Items = new SerializableDictionary<int, Objective>();
            this.Kills = new SerializableDictionary<int, Objective>();
            
            base.ParsePages();

            // handle the quest specific details
            this.ParseQuestData();

        }

        /// <summary>
        /// Parses the item data.
        /// </summary>
        private void ParseQuestData()
        {
            // get details from the wowhead page
            SerializableDictionary<int, Objective> details = GetWowheadQuestDetails(this.originalPage);
            SerializableDictionary<int, Objective> transDetails = GetWowheadQuestDetails(this.transPage);

            // create objectives out of the detail info
            foreach (Objective detail in details.Values)
            {
                if (!transDetails.ContainsKey(detail.Id))
                {
                    continue;
                }

                Objective transDetail = transDetails[detail.Id];

                // set the translation
                detail.Name.Language = DataProvider.DataLanguage;
                detail.Name.Translated = transDetail.Name.Original;
                detail.KillText.Language = DataProvider.DataLanguage;
                detail.KillText.Translated = transDetail.KillText.Original;

                // add objective base name translation 
                if (!DataProvider.TranslationsDict.ContainsKey(detail.Name.Original))
                    DataProvider.TranslationsDict.Add(detail.Name.Original, detail.Name.Translated);

                // add full objective translation 
                string fullObjectiveName = string.Format(@"{0}{1}: {2}/{2}",detail.Name.Original, detail.KillText.Original, detail.Count);
                if (!DataProvider.TranslationsDict.ContainsKey(fullObjectiveName))
                {
                    string transFullObjectiveName = string.Format(@"{0}{1}: {2}/{2}", detail.Name.Translated, detail.KillText.Translated, detail.Count);
                    DataProvider.TranslationsDict.Add(fullObjectiveName, transFullObjectiveName);
                }

                if (detail.Type == RequestType.Item)
                {
                    if (!this.Items.ContainsKey(detail.Id))
                    {
                        this.Items.Add(detail.Id, detail);
                        // id item new -> get info from server
                        if (!DataProvider.ItemDict.ContainsKey(detail.Id))
                        {
                            // parses the item and adds all dropping creatures to the dict.
                            WowheadItemParser.Parse(detail.Id, this.language);
                        }
                    }
                }
                else if (detail.Type == RequestType.Npc)
                {
                    if (!this.Kills.ContainsKey(detail.Id))
                    {
                        this.Kills.Add(detail.Id, detail);
                    }
                }
            }
        }

        /// <summary>
        /// Gets the wowhead quest details.
        /// </summary>
        /// <param name="pageContent">Content of the page.</param>
        /// <returns>A dictionary with all details, if nothing found an empty dict</returns>
        private SerializableDictionary<int, Objective> GetWowheadQuestDetails(string pageContent)
        {
            SerializableDictionary<int, Objective> returnVal = new SerializableDictionary<int, Objective>();

            string iconlist = this.GetIconlist(pageContent);

            if (iconlist != string.Empty)
            {
                string regex = @"\<a\s*href=""/\?(?<type>\w+)=(?<id>\d+)""\>(?<name>[^<]+)\</a\>(?<kill>[^(&<]+)?[^(\n\r]*(?:\((?<quantity>\d+)\))?";

                MatchCollection matches = Regex.Matches(iconlist, regex, RegexOptions.IgnoreCase);

                foreach (Match match in matches)
                {
                    Objective matchDetail = this.HandleDetailMatch(match);
                    returnVal.Add(matchDetail.Id, matchDetail);
                }
            }

            return returnVal;
        }

        /// <summary>
        /// Handles the detail match.
        /// </summary>
        /// <param name="match">The match.</param>
        /// <returns></returns>
        private Objective HandleDetailMatch(Match match)
        {
            // get all information from the regex match
            return new Objective()
            {
                Type = match.Groups[@"type"].Value,
                Id = Convert.ToInt32(match.Groups[@"id"].Value),
                Name = new TranslatedPair() { Original = match.Groups[@"name"].Value },
                Count = match.Groups[@"quantity"].Success ? Convert.ToInt32(match.Groups[@"quantity"].Value) : 1,
                KillText = new TranslatedPair() { Original = match.Groups[@"kill"].Success ? match.Groups[@"kill"].Value : string.Empty }
            };
        }

        /// <summary>
        /// Gets the itemlist from the pages content.
        /// </summary>
        /// <param name="pageContent">Content of the page.</param>
        /// <returns>Itemlist html if found otherwise <seealso cref="string.Empty"/></returns>
        private string GetIconlist(string pageContent)
        {
            string regex = @"class=""iconlist"">[\r\n]*(?<iconlines>(?:.|[\r\n])*?)[\r\n]*\</table\>";
            // this is safe because nobody returns null on failure
            return Regex.Match(pageContent, regex, RegexOptions.IgnoreCase | RegexOptions.Multiline).Groups[@"iconlines"].Value;
        }

        #endregion methods

        #region properties

        /// <summary>
        /// Gets or sets the items on the page.
        /// </summary>
        /// <value>The items.</value>
        [XmlElement]
        public SerializableDictionary<int, Objective> Items { get; set; }

        /// <summary>
        /// Gets or sets the kills.
        /// </summary>
        /// <value>The kills.</value>
        [XmlElement]
        public SerializableDictionary<int, Objective> Kills { get; set; }

        #endregion properties

    }
}
